unit multiboot;

interface

uses bootstrap, multibootfs;

implementation

uses vfs, memoryutils;

{$asmmode att}

procedure text_start; external name '__text_start__';
procedure data_end; external name '__data_end__';
procedure bss_end; external name '__bss_end__';
procedure Entry2; external name 'ExecEntry';

procedure Signature;
const
 MULTIBOOT_PAGE_ALIGN = 1;
 MULTIBOOT_MEMORY_INFO = 2;
 MULTIBOOT_AOUT_KLUDGE = 1 shl 16;
 
 MultibootSig = $1BADB002;
 MultibootFlags = {MULTIBOOT_PAGE_ALIGN or }MULTIBOOT_MEMORY_INFO or MULTIBOOT_AOUT_KLUDGE;
 MultibootChksum = -(MultibootSig + MultibootFlags);
label abc;
begin
	asm
      .init
abc:
      .long MultibootSig
      .long MultibootFlags
      .long MultibootChksum
		//AOUT kludge
		.long abc
		.long text_start
		.long data_end
		.long bss_end
		.long Entry2
		
		.text
	end;
end;

{$asmmode intel}

type
 PMBModule = ^TMBModule;
 TMBModule = packed record
  mod_start,
  mod_end: PtrUInt;
  name: pchar;
  reserved: DWord;
 end;
 
 PMemoryMap = ^TMemoryMap;
 TMemoryMap = packed record
  size,
  base_lower,
  base_upper,
  length_lower,
  length_upper,
  mtype: DWord;
 end;

 PElfSectionHeaderTable = ^TElfSectionHeaderTable;
 TElfSectionHeaderTable = packed record
  num,
  size,
  addr,
  shndx: DWord;
 end; 

 PMultibootinfo = ^TMultibootinfo;
 TMultiBootInfo = packed record
  Flags,
  MemLower, MemUpper,
  BootDevice,
  CmdLine,
  ModuleCount: DWord;
  ModuleAddress: PMBModule;
  ElfSection: TElfSectionHeaderTable;
  MMapLength: longint;
  MMapAddress: PMemoryMap;
 end;

const
 KernelStackSize = 1024;
var
 KernelStack: array[0..KernelStackSize-1] of byte;
 mbinfo: TMultibootinfo;
 t: PMultibootinfo;
 
 memchain: TMemoryChain;

procedure EndC; external name '__bss_end__';

procedure HandleMultiboot;
var i, l: longint;
	 c: pmemorymap;
begin
	InitializeChain(MemChain);
	
	//Register memory blocks
	c := pmemorymap(ptruint(mbinfo.mmapaddress) and $FFFFFFFF);
   l := (mbinfo.mmaplength div sizeof(Tmemorymap));
   for i := 0 to l-1 do
		if c[i].mtype = 1 then
			AddBlock(memchain, pointer(ptruint(c[i].base_lower) and $FFFFFFFF), c[i].length_lower);
	
	//Reserve 1MB + kernel
	RemoveBlock(memchain, nil, ptruint(@endc));
	
	//Multiboot module table
	RemoveBlock(memchain, MBInfo.ModuleAddress, MBinfo.ModuleCount*sizeof(TMBModule));
	
	//Reserve modules
	for i := 0 to MBinfo.ModuleCount-1 do
	begin
		RemoveBlock(memchain, MBInfo.ModuleAddress[i].name, strlen(MBInfo.ModuleAddress[i].name)+1);
		RemoveBlock(memchain, pointer(ptruint(MBInfo.ModuleAddress[i].Mod_start)), MBInfo.ModuleAddress[i].Mod_end-MBInfo.ModuleAddress[i].Mod_start);
	end;
	
	for i := 0 to MemChain.BlockCount-1 do
		RegisterBlock(MemChain.Free[i].Address, MemChain.Free[i].Size);
end;

procedure AddModules;
var i: PtrInt;
	 fs: TMBFS;
begin
	fs := TMBFS.Create;
	for i := 0 to MBinfo.ModuleCount-1 do 
		fs.AddFile(mbinfo.Moduleaddress[i].name, Pointer(MBInfo.ModuleAddress[i].Mod_start), MBInfo.ModuleAddress[i].Mod_end-MBInfo.ModuleAddress[i].Mod_start);
	
	if not assigned(VFSManager) then VFSManager := TVFS.Create;
	
	VFSManager.AddFilesystem('boot', fs);
end;

procedure Entry; nostackframe; [public, alias: 'ExecEntry'];
begin
	asm
		mov esp, offset KernelStack+KernelStackSize
		mov ebp, esp
		
		mov t,ebx
	end;
	mbinfo := t^;
	
	FillWord(pbyte($b8000)^, 80*25, $720);
	
	HandleMultiboot;
	AddModules;
	
	EnterKernel;
end;

end.
